package com.heima.wemedia.service.impl;

import com.alibaba.fastjson.JSONArray;
import com.heima.aliyun.GreenImageScan;
import com.heima.aliyun.GreenTextScan;
import com.heima.common.exception.CustException;
import com.heima.feigns.AdminFeign;
import com.heima.feigns.ArticleFeign;
import com.heima.model.common.constants.admin.WemediaConstants;
import com.heima.model.common.constants.message.PublishArticleConstants;
import com.heima.model.common.dtos.ResponseResult;
import com.heima.model.common.enums.AppHttpCodeEnum;
import com.heima.model.wemedia.dtos.NewsAuthDTO;
import com.heima.model.wemedia.pojos.WmNews;
import com.heima.utils.common.SensitiveWordUtil;
import com.heima.wemedia.mapper.WmNewsMapper;
import com.heima.wemedia.service.WmNewsAutoScanService;
import com.heima.wemedia.service.WmNewsService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessagePostProcessor;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@Slf4j
public class WmNewsAutoScanServiceImpl implements WmNewsAutoScanService {
    @Autowired
    WmNewsMapper wmNewsMapper;

    @Value("${file.oss.web-site}")
    String webSite;

    @Autowired
    RabbitTemplate rabbitTemplate;

    @Autowired
    WmNewsService wmNewsService;


    @Override
    public void autoScanWmNews(Integer newId) {
        log.info("接收到文章自动审核消息   消息的内容为==>{}", newId);
        // 1. 检查参数  (newsId  )
        if (newId == null) {
            CustException.cust(AppHttpCodeEnum.PARAM_INVALID, "文章id 不能为空");
        }
        // 2. 根据文章id查询文章数据
        WmNews wmNews = wmNewsMapper.selectById(newId);

        //    文章是否为空
        if (wmNews == null) {
            log.error("文章自动审核失败    未查询到相关文章==>{}", newId);
            CustException.cust(AppHttpCodeEnum.DATA_NOT_EXIST, "未查询到文章信息");
        }
        //    检查文章状态是否为待审核
        if (!WemediaConstants.WM_NEWS_SUMMIT_STATUS.equals(wmNews.getStatus())) {
            log.error("文章自动审核失败   文章的状态不是待审核状态==>{}", newId);
            CustException.cust(AppHttpCodeEnum.DATA_NOT_ALLOW, "文章状态不是待审核状态，无法进行审核操作");
        }
        // 3. 审核文章
        // 3.1 抽取出 文章中 所有的文本内容 和 图片内容
        // Map<String,Object>  key:  content 文本内容 , value: String  文本内容   key: images 图片集合, List<String> 图片url集合
        Map<String, Object> contentAndImages = handleTextAndImage(wmNews);
        //3.2  DFA敏感词审核
        boolean isSensitive = handleSensitive((String) contentAndImages.get("content"), wmNews);
        if (!isSensitive) return; // 敏感词审核失败  直接结束方法
        log.info("文章自动审核   敏感词审核通过==>{}", newId);
        // 3.3  阿里云文本内容审核
        boolean isTextScan = handleText((String) contentAndImages.get("content"), wmNews);
        if (!isTextScan) return;
        log.info("文章自动审核   阿里云文本审核通过==>{}", newId);
        // 3.4  阿里云图片审核
        boolean isImageScan = handleImage((List<String>) contentAndImages.get("images"), wmNews);
        if (!isImageScan) return;
        log.info("文章自动审核   阿里云图片审核通过==>{}", newId);
        // 4. 将文章状态修改为 8 审核通过
        updateWmNews(wmNews, WmNews.Status.SUCCESS.getCode(), "审核通过");
        //  5. 根据文章的发布时间  延时发布文章
        long publishTime = wmNews.getPublishTime().getTime(); // 发布时间
        long nowTime = System.currentTimeMillis(); // 当前时间
        long remainTime = (publishTime - nowTime);  // 距离发布文章还有多长时间
        rabbitTemplate.convertAndSend(PublishArticleConstants.DELAY_DIRECT_EXCHANGE
                , PublishArticleConstants.PUBLISH_ARTICLE_ROUTE_KEY
                , wmNews.getId()
                , new MessagePostProcessor() {
                    /**
                     * 消息的后置处理器
                     * @param message
                     * @return
                     * @throws AmqpException
                     */
                    @Override
                    public Message postProcessMessage(Message message) throws AmqpException {
                        // 对消息做后置处理 设置消息头 x-delay , 延迟的时间
                        message.getMessageProperties().setHeader("x-delay", remainTime <= 0 ? 0 : remainTime);
                        return message;
                    }
                }
        );
    }

    @Autowired
    GreenImageScan greenImageScan;

    /**
     * 阿里云 审核图片内容安全
     * block  stauts=2    review  stauts=3
     *
     * @param images
     * @param wmNews
     * @return
     */
    private boolean handleImage(List<String> images, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenImageScan.imageUrlScan(images);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "review":
                    updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "图片中有不确定因素，需要人工审核");
                    flag = false;
                    break;
                case "block":
                    updateWmNews(wmNews, WmNews.Status.FAIL.getCode(), "图片中有违规信息，审核失败");
                    flag = false;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "阿里云调用失败，转人工审核");
            flag = false;
        }
        return flag;
    }


    @Autowired
    GreenTextScan greenTextScan;

    /**
     * 调用阿里云内容安全接口  审核文本内容是否有违规信息
     * 如果阿里云返回的是 block  文章 status: 2        review   文章 status: 3    出现异常 status 3
     *
     * @param content
     * @param wmNews
     * @return
     */
    private boolean handleText(String content, WmNews wmNews) {
        boolean flag = true;
        try {
            Map map = greenTextScan.greenTextScan(content);
            String suggestion = (String) map.get("suggestion");
            switch (suggestion) {
                case "review":
                    updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "文本中有不确定因素，需要人工审核");
                    flag = false;
                    break;
                case "block":
                    updateWmNews(wmNews, WmNews.Status.FAIL.getCode(), "文本中有违规内容，审核失败");
                    flag = false;
                    break;
            }
        } catch (Exception e) {
            e.printStackTrace();
            updateWmNews(wmNews, WmNews.Status.ADMIN_AUTH.getCode(), "调用阿里云接口失败，转为人工审核");
            flag = false;
        }
        return flag;
    }

    @Autowired
    AdminFeign adminFeign;

    /**
     * 成功返回 true  失败返回false   并且失败  需要修改自媒体文章 状态: 2
     *
     * @param content
     * @param wmNews
     * @return
     */
    private boolean handleSensitive(String content, WmNews wmNews) {
        // 1. 查询敏感词列表
        ResponseResult<List<String>> sensitivesResult = adminFeign.sensitives();
        if (sensitivesResult.getCode().intValue() != 0) {
            CustException.cust(AppHttpCodeEnum.REMOTE_SERVER_ERROR, "远程调用敏感词列表方法失败");
        }
        List<String> sensitives = sensitivesResult.getData();
        // 2. DFA工具类 初始化DFA数据模型
        SensitiveWordUtil.initMap(sensitives);
        // 3. 使用DFA算法进行敏感词审核
        Map<String, Integer> map = SensitiveWordUtil.matchWords(content);
        // 4. 根据审核结果 修改wmNews
        if (map != null && map.size() > 0) { // 有敏感词
            // 修改文章状态为 2
            updateWmNews(wmNews, WmNews.Status.FAIL.getCode(), "文本内容包含自管理的敏感词: " + map.toString());
            return false;
        }
        return true;
    }

    /**
     * 修改文章状态
     *
     * @param wmNews 文章
     * @param status 状态
     * @param msg    原因
     */
    public void updateWmNews(WmNews wmNews, Short status, String msg) {
        wmNews.setReason(msg);
        wmNews.setStatus(status);
        wmNewsMapper.updateById(wmNews);
    }

    /**
     * 抽取文章中 所有 文本内容  和  所有 图片url
     *
     * @param wmNews 文章对象
     * @return Map<String, Object>  key:  content 文本内容 , value: String  文本内容   key: images 图片集合, List<String> 图片url集合
     */
    private Map<String, Object> handleTextAndImage(WmNews wmNews) {
        // [{type: text , value: 文本},{type:image,value: url},{}]
        Map<String, Object> result = new HashMap<>();

        String contentJson = wmNews.getContent();
        List<Map> contentMap = JSONArray.parseArray(contentJson, Map.class);

        // 1. 抽取  文本内容
        StringBuilder content = new StringBuilder(wmNews.getTitle());
        // 1.1  便利 contentMap集合  找到  type = text  遍历所有内容 拼接到content字符串上
        contentMap.stream()
                .filter(map -> "text".equals(map.get("type")))
                .map(o -> (String) o.get("value"))
                .forEach((text) -> content.append("_hmtt_" + text)); // 外面的花很美_hmtt_国家很强大
        result.put("content", content.toString());
        // 2. 图片集合  content中包含的所有图片     封面中包含的所有图片      放到同一个集合    最后去重
        List<String> images = contentMap.stream()
                .filter(map -> "image".equals(map.get("type")))
                .map(o -> (String) o.get("value"))
                .collect(Collectors.toList());
        if (StringUtils.isNotBlank(wmNews.getImages())) {
            //  url1,url2,url3
            List<String> coverImage = Arrays.stream(wmNews.getImages().split(","))  // 得到封面流
                    .map(url -> webSite + url)
                    .collect(Collectors.toList());
            images.addAll(coverImage);
        }
        // 去重
        images = images.stream().distinct().collect(Collectors.toList());
        result.put("images", images);
        return result;
    }
}
